package cn.yx.common.core.util.io;

import cn.yx.common.core.constant.FileConstant;
import cn.yx.common.core.constant.message.io.ZipUtilMessageKey;
import cn.yx.common.core.exception.CommonException;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * <p>zip压缩工具类</p>
 *
 * @author Wgssmart
 */
@Slf4j
public final class ZipUtil {

    private ZipUtil() {
    }

    /**
     * 压缩指定文件到指定zip
     *
     * @param files     需要压缩的文件列表
     * @param toZipPath 压缩文件保存路径
     */
    public static void fromFile(List<File> files, String toZipPath) {
        try (FileOutputStream fileOutputStream = new FileOutputStream(toZipPath);
             ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream)) {
            for (File file : files) {
                writeZipEntry(zipOutputStream, file.getName(), file);
            }
        } catch (IOException e) {
            log.error("zip from file error: {}, to zip path: {}", e.getMessage(), toZipPath);
            throw new CommonException(ZipUtilMessageKey.ZIP_FROM_FILE_ERROR, e);
        }
    }

    /**
     * 压缩指定路径的文件到zip
     *
     * @param filePaths 需要压缩的文件路径列表
     * @param toZipPath 压缩文件保存路径
     */
    public static void fromFilePath(List<String> filePaths, String toZipPath) {
        List<File> files = new ArrayList<>();
        for (String filePath : filePaths) {
            File file = new File(filePath);
            files.add(file);
        }
        fromFile(files, toZipPath);
    }

    /**
     * 压缩指定的输入流到zip文件
     *
     * @param inputStreamMap Map中的key为每个输入流指定文件名
     * @param toZipPath      压缩文件保存路径
     */
    public static void fromInput(Map<String, InputStream> inputStreamMap, String toZipPath) {
        try (FileOutputStream fileOutputStream = new FileOutputStream(toZipPath);
             ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream)) {
            for (Map.Entry<String, InputStream> entry : inputStreamMap.entrySet()) {
                writeZipEntry(zipOutputStream, entry.getKey(), entry.getValue());
            }
        } catch (IOException e) {
            log.error("zip from input stream error: {}, to zip path: {}", e.getMessage(), toZipPath);
            throw new CommonException(ZipUtilMessageKey.ZIP_FROM_INPUT_STREAM_ERROR, e);
        }
    }

    /**
     * 压缩指定文件到字节输出流
     *
     * @param files 压缩文件列表
     * @return 压缩文件对应的字节输出流
     */
    public static ByteArrayOutputStream fromFileToOutput(List<File> files) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
            for (File file : files) {
                writeZipEntry(zipOutputStream, file.getName(), file);
            }
        } catch (IOException e) {
            log.error("zip from file to output stream error: {}", e.getMessage());
            throw new CommonException(ZipUtilMessageKey.ZIP_TO_OUTPUT_STREAM_ERROR, e);
        }
        return byteArrayOutputStream;
    }

    /**
     * 压缩指定路径的文件到字节输出流
     *
     * @param filePaths 文件路径列表
     * @return 压缩文件对应的字节输出流
     */
    public static ByteArrayOutputStream fromFilePathToOutput(List<String> filePaths) {
        List<File> files = new ArrayList<>();
        for (String filePath : filePaths) {
            File file = new File(filePath);
            files.add(file);
        }
        return fromFileToOutput(files);
    }

    /**
     * 压缩指定的输入流到字节输出流
     *
     * @param inputStreamMap Map中的key为每个输入流指定文件名
     * @return 压缩文件对应的字节输出流
     */
    public static ByteArrayOutputStream fromInputToOutput(Map<String, InputStream> inputStreamMap) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
            for (Map.Entry<String, InputStream> entry : inputStreamMap.entrySet()) {
                writeZipEntry(zipOutputStream, entry.getKey(), entry.getValue());
            }
        } catch (IOException e) {
            log.error("zip from input stream to output stream error: {}", e.getMessage());
            throw new CommonException(ZipUtilMessageKey.ZIP_TO_OUTPUT_STREAM_ERROR, e);
        }
        return byteArrayOutputStream;
    }

    /**
     * 把文件写入到ZipOutputStream
     *
     * @param zipOutputStream ZipOutputStream输出流对象
     * @param fileName        文件名名称，作为ZipEntry的条目名称
     * @param file            文件
     * @throws IOException IOException
     */
    private static void writeZipEntry(ZipOutputStream zipOutputStream, String fileName, File file) throws IOException {
        ZipEntry zipEntry = new ZipEntry(fileName);
        zipOutputStream.putNextEntry(zipEntry);
        try (InputStream inputStream = new FileInputStream(file)) {
            byte[] bytes = new byte[FileConstant.DEFAULT_BUFFER_SIZE];
            int len;
            while ((len = inputStream.read(bytes)) != -1) {
                zipOutputStream.write(bytes, 0, len);
            }
        }
        zipOutputStream.closeEntry();
    }

    /**
     * 把InputStream定入到ZipOutputStream
     *
     * @param zipOutputStream  ZipOutputStream输出流对象
     * @param zipEntryName     ZipEntry条目名称
     * @param entryInputStream 文件输入流
     * @throws IOException IOException
     */
    private static void writeZipEntry(ZipOutputStream zipOutputStream, String zipEntryName,
                                      InputStream entryInputStream) throws IOException {
        ZipEntry zipEntry = new ZipEntry(zipEntryName);
        zipOutputStream.putNextEntry(zipEntry);
        byte[] bytes = new byte[FileConstant.DEFAULT_BUFFER_SIZE];
        int len;
        while ((len = entryInputStream.read(bytes)) != -1) {
            zipOutputStream.write(bytes, 0, len);
        }
        entryInputStream.close();
        zipOutputStream.closeEntry();
    }

}
